/*
 * SPDX-License-Identifier: Apache-2.0
 */

package org.hyperledger.fabric.samples.assettransfer;

import java.util.Date;
import java.util.ArrayList;
import java.util.List;

import org.hyperledger.fabric.contract.Context;
import org.hyperledger.fabric.contract.ContractInterface;
import org.hyperledger.fabric.contract.annotation.Contact;
import org.hyperledger.fabric.contract.annotation.Contract;
import org.hyperledger.fabric.contract.annotation.Default;
import org.hyperledger.fabric.contract.annotation.Info;
import org.hyperledger.fabric.contract.annotation.License;
import org.hyperledger.fabric.contract.annotation.Transaction;
import org.hyperledger.fabric.shim.ChaincodeException;
import org.hyperledger.fabric.shim.ChaincodeStub;
import org.hyperledger.fabric.shim.ledger.KeyValue;
import org.hyperledger.fabric.shim.ledger.QueryResultsIterator;

import com.owlike.genson.Genson;

@Contract(
        name = "basic",
        info = @Info(
                title = "Asset Transfer",
                description = "The hyperlegendary asset transfer",
                version = "0.0.1-SNAPSHOT",
                license = @License(
                        name = "Apache 2.0 License",
                        url = "http://www.apache.org/licenses/LICENSE-2.0.html"),
                contact = @Contact(
                        email = "a.transfer@example.com",
                        name = "Adrian Transfer",
                        url = "https://hyperledger.example.com")))
@Default
public final class AssetTransfer implements ContractInterface {

    private final Genson genson = new Genson();

    private enum AssetTransferErrors {
        ASSET_NOT_FOUND,
        ASSET_ALREADY_EXISTS
    }

    /**
     * Creates some initial assets on the ledger.
     *
     * @param ctx the transaction context
     */
    @Transaction(intent = Transaction.TYPE.SUBMIT)
    public void InitLedger(final Context ctx) {
        ChaincodeStub stub = ctx.getStub();

        CreateAsset(ctx, "1", "1", "京A11111", "江苏", 100, "yes", "郑州", "江苏", 0, "2021-03-01 10:52:03", "2021-03-03 10:52:13", "dpNum_1");
        CreateAsset(ctx, "2", "2", "京A22222", "河北", 500, "yes", "保定", "沧州", 0, "2021-05-01 10:53:53", "2021-05-02 10:54:04", "dpNum_2");
        CreateAsset(ctx, "3", "3", "京A33333", "南昌", 700, "yes", "武汉", "南昌", 0, "2021-05-19 10:57:22", "2021-05-21 10:57:33", "dpNum_3");

//        CreateAsset(ctx, "asset1", "a0001", "京ABC", "北京", 300, "?", "通县", "雄安", 0, new Date().toString(), new Date().toString(), "?");
//        CreateAsset(ctx, "asset2", "a0002", "冀E20", "河北", 400, "?", "石家庄", "保定", 1, new Date().toString(), new Date().toString(), "?");
//        CreateAsset(ctx, "asset3", "a0003", "晋XBD", "北京", 500, "?", "海淀区", "东城区", 2, new Date().toString(), new Date().toString(), "?");
//        CreateAsset(ctx, "asset4", "a0004", "黑10K", "黑龙江", 600, "?", "哈尔滨", "哈尔滨", 0, new Date().toString(), new Date().toString(), "?");
//        CreateAsset(ctx, "asset5", "a0005", "鲁B15", "北京", 700, "?", "雄安", "门头沟区", 1, new Date().toString(), new Date().toString(), "?");
//        CreateAsset(ctx, "asset6", "a0006", "赣1A4", "河南", 700, "?", "石家庄", "郑州", 1, new Date().toString(), new Date().toString(), "?");
//	   CreateAsset(ctx, "asset1", "a0001", "京ABC", "北京", 300, "?", "通县", "雄安", 0, "?");
//        CreateAsset(ctx, "asset2", "a0002", "冀E20", "河北", 400, "?", "石家庄", "保定", 1, "?");
//        CreateAsset(ctx, "asset3", "a0003", "晋XBD", "北京", 500, "?", "海淀区", "东城区", 2, "?");
//        CreateAsset(ctx, "asset4", "a0004", "黑10K", "黑龙江", 600, "?", "哈尔滨", "哈尔滨", 0, "?");
//        CreateAsset(ctx, "asset5", "a0005", "鲁B15", "北京", 700, "?", "雄安", "门头沟区", 1, "?");
//        CreateAsset(ctx, "asset6", "a0006", "赣1A4", "河南", 700, "?", "石家庄", "郑州", 1, "?");

    }

    /**
     * Creates a new asset on the ledger.
     */
    @Transaction(intent = Transaction.TYPE.SUBMIT)
    public Asset CreateAsset(final Context ctx,
                             final String assetID,
                             final String rID,
                             final String cId,
                             final String aProvince,
                             final float amount,
                             final String aProof,
                             final String pStart,
                             final String pEnd,
                             final int status,
                             final String aStart,
                             final String aEnd,
                             final String dpNum) {
        ChaincodeStub stub = ctx.getStub();

        if (AssetExists(ctx, assetID)) {
            String errorMessage = String.format("Asset %s already exists", assetID);
            System.out.println(errorMessage);
            throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_ALREADY_EXISTS.toString());
        }

        Asset asset = new Asset(assetID, rID, cId, aProvince, amount, aProof, pStart, pEnd, status, aStart, aEnd, dpNum);
//        Asset asset = new Asset(assetID, rID, cId, aProvince, amount, aProof, pStart, pEnd, status, dpNum);
        String assetJSON = genson.serialize(asset);
        stub.putStringState(assetID, assetJSON);

        return asset;
    }

    /**
     * Retrieves an asset with the specified ID from the ledger.
     *
     * @param ctx the transaction context
     * @param assetID the ID of the asset
     * @return the asset found on the ledger if there was one
     */
    @Transaction(intent = Transaction.TYPE.EVALUATE)
    public String ReadAsset(final Context ctx, final String assetID) {
        ChaincodeStub stub = ctx.getStub();
        String assetJSON = stub.getStringState(assetID);

        if (assetJSON == null || assetJSON.isEmpty()) {
            String errorMessage = String.format("Asset %s does not exist", assetID);
            System.out.println(errorMessage);
            throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
        }



        Asset asset = genson.deserialize(assetJSON, Asset.class);

        return assetJSON;
    }

    /**
     * Updates the properties of an asset on the ledger.
     *
     * @return the transferred asset
     */
    @Transaction(intent = Transaction.TYPE.SUBMIT)
    public Asset UpdateAsset(final Context ctx,
                             final String assetID,
                             final String rID,
                             final String cId,
                             final String aProvince,
                             final float amount,
                             final String aProof,
                             final String pStart,
                             final String pEnd,
                             final int status,
                             final String aStart,
                             final String aEnd,
                             final String dpNum) {
        ChaincodeStub stub = ctx.getStub();

        if (!AssetExists(ctx, assetID)) {
            String errorMessage = String.format("Asset %s does not exist", assetID);
            System.out.println(errorMessage);
            throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
        }

        Asset newAsset = new Asset(assetID, rID, cId, aProvince, amount, aProof, pStart, pEnd, status, aStart, aEnd, dpNum);
//        Asset newAsset = new Asset(assetID, rID, cId, aProvince, amount, aProof, pStart, pEnd, status, dpNum);
        String newAssetJSON = genson.serialize(newAsset);
        stub.putStringState(assetID, newAssetJSON);

        return newAsset;
    }

    /**
     * Deletes asset on the ledger.
     *
     * @param ctx the transaction context
     * @param assetID the ID of the asset being deleted
     */
    @Transaction(intent = Transaction.TYPE.SUBMIT)
    public void DeleteAsset(final Context ctx, final String assetID) {
        ChaincodeStub stub = ctx.getStub();

        if (!AssetExists(ctx, assetID)) {
            String errorMessage = String.format("Asset %s does not exist", assetID);
            System.out.println(errorMessage);
            throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
        }

        stub.delState(assetID);
    }

    /**
     * Checks the existence of the asset on the ledger
     *
     * @param ctx the transaction context
     * @param assetID the ID of the asset
     * @return boolean indicating the existence of the asset
     */
    @Transaction(intent = Transaction.TYPE.EVALUATE)
    public boolean AssetExists(final Context ctx, final String assetID) {
        ChaincodeStub stub = ctx.getStub();
        String assetJSON = stub.getStringState(assetID);

        return (assetJSON != null && !assetJSON.isEmpty());
    }

    /**
     * Changes the owner of a asset on the ledger.
     *
     * @return the updated asset
     */
    @Transaction(intent = Transaction.TYPE.SUBMIT)
    public Asset TransferAsset(final Context ctx, final String assetID, final String newOwner) {
//        ChaincodeStub stub = ctx.getStub();
//        String assetJSON = stub.getStringState(assetID);
//
//        if (assetJSON == null || assetJSON.isEmpty()) {
//            String errorMessage = String.format("Asset %s does not exist", assetID);
//            System.out.println(errorMessage);
//            throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
//        }
//
//        Asset asset = genson.deserialize(assetJSON, Asset.class);
//
//        Asset newAsset = new Asset(asset.getAssetID(),
//                asset.getColor(),
//                asset.getSize(),
//                newOwner,
//                asset.getAppraisedValue());
//        String newAssetJSON = genson.serialize(newAsset);
//        stub.putStringState(assetID, newAssetJSON);

//        return newAsset;
        return null;
    }

    /**
     * Retrieves all assets from the ledger.
     *
     * @param ctx the transaction context
     * @return array of assets found on the ledger
     */
    @Transaction(intent = Transaction.TYPE.EVALUATE)
    public String GetAllAssets(final Context ctx) {
        ChaincodeStub stub = ctx.getStub();

        List<Asset> queryResults = new ArrayList<Asset>();

        // To retrieve all assets from the ledger use getStateByRange with empty startKey & endKey.
        // Giving empty startKey & endKey is interpreted as all the keys from beginning to end.
        // As another example, if you use startKey = 'asset0', endKey = 'asset9' ,
        // then getStateByRange will retrieve asset with keys between asset0 (inclusive) and asset9 (exclusive) in lexical order.
        QueryResultsIterator<KeyValue> results = stub.getStateByRange("", "");

        for (KeyValue result: results) {
            Asset asset = genson.deserialize(result.getStringValue(), Asset.class);
            queryResults.add(asset);
            System.out.println(asset.toString());
        }

        final String response = genson.serialize(queryResults);

        return response;
    }
}
